/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This source code is subject to terms and conditions of the Shared Source License
* for DSL Editor PowerToy. A copy of the license can be found in the License.htm file
* at the root of this distribution. If you can not locate the Shared Source License
* for DSL Editor PowerToy, please obtain a copy from: http://www.codeplex.com/dsltreegrideditor/Project/License.aspx.
* By using this source code in any fashion, you are agreeing to be bound by
* the terms of the Shared Source License for DSL Editor PowerToy.
*
* You must not remove this notice, or any other, from this software.
*
* **********************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

using Microsoft.VisualStudio.Modeling.Shell;

using DslEditorPowerToy.Controls.Editors;

namespace DslEditorPowerToy.Controls.Languages
{
    /// <summary>
    /// Control to manage runtime loading, and interaction between container and leafed control
    /// </summary>
    public partial class EditorHostControl : UserControl, IMonitorSelection, ISelectionService 
    {
        private Dictionary<Guid, IHostedEditorDefinition> _editors = null;
        private Assembly _editorDeclaringAssembly = null;
        private ModelingDocData _currentDocData = null;

        #region Constructors
        public EditorHostControl(Assembly editorDeclaringAssembly)
        {
            InitializeComponent();

            this._editors = new Dictionary<Guid, IHostedEditorDefinition>();
            this._editorDeclaringAssembly = editorDeclaringAssembly;
        }

        #endregion

        #region IMonitorSelection Members

        public void NotifySelectionChanged(ISelectionProvider selectionProvider, SelectionChangeSource source)
        {
            //Forward to contained control
            this.leafedEditorHost.NotifySelectionChanged(selectionProvider, source);
        }
        #endregion

        #region ISelectionService Members

        public bool GetComponentSelected(object component)
        {
            return this.leafedEditorHost.GetComponentSelected(component);
        }
        public System.Collections.ICollection GetSelectedComponents()
        {
            return this.leafedEditorHost.GetSelectedComponents();
        }
        public object PrimarySelection
        {
            get
            {
                return this.leafedEditorHost.PrimarySelection;
            }
        }
        
        public event EventHandler SelectionChanged;
        public event EventHandler SelectionChanging;
        
        public int SelectionCount
        {
            get
            {
                return this.leafedEditorHost.SelectionCount;
            }
        }
        public void SetSelectedComponents(System.Collections.ICollection components, SelectionTypes selectionType)
        {
            this.leafedEditorHost.SetSelectedComponents(components, selectionType);
        }
        public void SetSelectedComponents(System.Collections.ICollection components)
        {
            this.leafedEditorHost.SetSelectedComponents(components);
        }

        #endregion

        #region Protected Properties
        /// <summary>
        /// Gets the configured editors
        /// </summary>
        protected Dictionary<Guid, IHostedEditorDefinition> Editors
        {
            get
            {
                return this._editors;
            }
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the current editor
        /// </summary>
        public IHostedEditorDefinition CurrentEditor
        {
            get
            {
                HostedEditor editor = this.leafedEditorHost.CurrentEditor;
                if (null == editor)
                    return null;

                if (false == this._editors.ContainsKey(editor.Id))
                    return null;

                return this._editors[editor.Id];
            }
        }
        /// <summary>
        /// Gets and sets the navigation style
        /// </summary>
        public NavigationOrientationStyle NavigationStyle
        {
            get
            {
                return this.leafedEditorHost.NavigationStyle;
            }
            set
            {
                this.leafedEditorHost.NavigationStyle = value;
            }
        }
        /// <summary>
        /// Gets/Sets the doc view for the DSL
        /// </summary>
        [CLSCompliant(false)]
        public ModelingDocData CurrentDocData
        {
            get
            {
                return this._currentDocData;
            }
            set
            {
                //Raise event
                OnDocDataChanged(this._currentDocData, value);
                
                this._currentDocData = value;
            }
        }
        #endregion

        #region Public Events
        public event EventHandler EditorChanged;
        #endregion

        #region Public Methods
        /// <summary>
        /// Activates specified editor
        /// </summary>
        public void ActivateEditor(Guid editorId)
        {
            //Activate the editor
            if (this._editors.ContainsKey(editorId))
                this.leafedEditorHost.ActivateEditor(editorId);
        }
        /// <summary>
        /// Returns a list of editors
        /// </summary>
        /// <returns></returns>
        public List<IHostedEditorDefinition> GetEditors()
        {
            List<IHostedEditorDefinition> editors = new List<IHostedEditorDefinition>();
            foreach (KeyValuePair<Guid, IHostedEditorDefinition> editor in this._editors)
                editors.Add(editor.Value);
            return editors;
        }
        #endregion

        #region Control Handlers
        private void MultiEditorHost_Load(object sender, EventArgs e)
        {
            //Load editors
            LoadEditors();

            //Initialise UI
            InitialiseEditorUI();
        }
        /// <summary>
        /// Handles the editor changed event from leafed editor
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void leafedEditorHost_EditorChanged(object sender, EventArgs e)
        {
            OnEditorChanged();
        }
        ///// <summary>
        ///// Handles the selection changed event from leafed editor
        ///// </summary>
        ///// <param name="sender"></param>
        private void leafedEditorHost_SelectionChanged(object sender, System.EventArgs e)
        {
            OnSelectionChanged();
        }
        /// <summary>
        /// Handles the selection changing event from leafed editor
        /// </summary>
        /// <param name="sender"></param>
        private void leafedEditorHost_SelectionChanging(object sender, System.EventArgs e)
        {
            OnSelectionChanging();
        }
        #endregion

        #region Protected Methods
        /// <summary>
        /// Raises the editor changed event
        /// </summary>
        protected void OnEditorChanged()
        {
            if (null != this.EditorChanged)
                this.EditorChanged(this, new EventArgs());
        }
        /// <summary>
        /// Raises the selection changed event
        /// </summary>
        protected void OnSelectionChanged()
        {
            if (null != this.SelectionChanged)
                this.SelectionChanged(this, new EventArgs());
        }
        /// <summary>
        /// Raises the selection changing event
        /// </summary>
        protected void OnSelectionChanging()
        {
            if (null != this.SelectionChanging)
                this.SelectionChanging(this, new EventArgs());
        }
        /// <summary>
        /// Passes focus to contained editor host
        /// </summary>
        /// <param name="e"></param>
        protected override void OnGotFocus(EventArgs e)
        {
            //Pass focus to contained editor host
            this.leafedEditorHost.Focus();

            base.OnGotFocus(e);
        }
        /// <summary>
        /// Capture TAB key and processes it on contained controls
        /// </summary>
        /// <param name="forward"></param>
        /// <returns></returns>
        protected override bool ProcessTabKey(bool forward)
        {
            //Pass focus around contained controls
            return base.SelectNextControl(base.ActiveControl, forward, true, true, true);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Loads the editors found in assembly
        /// </summary>
        private void LoadEditors()
        {
            //Ensure we have an assembly to load editor definitions from
            if (null == this._editorDeclaringAssembly)
                return;

            //Find all editor types in declaring assembly
            Type[] allTypes = this._editorDeclaringAssembly.GetTypes();
            foreach (Type type in allTypes)
            {
                //Check for attribute
                object[] customAttributes = type.GetCustomAttributes(typeof(HostedEditorDefinitionAttribute), false);
                if ((null != customAttributes)
                    && (customAttributes.GetLength(0) > 0))
                {
                    //Ensure correct interface
                    if (null != type.GetInterface(typeof(IHostedEditorDefinition).Name))
                    {
                        object typeInstance = CreateHostedEditorInstance(type);
                        if (null != typeInstance)
                        {
                            IHostedEditorDefinition editor = typeInstance as IHostedEditorDefinition;
                            this._editors.Add(editor.Id, editor);
                        }
                    }
                }
            }

            //Notify controls of initial document data
            OnDocDataChanged(null, this._currentDocData);
        }
        /// <summary>
        /// Initialises toolwindow UI with editors
        /// </summary>
        private void InitialiseEditorUI()
        {
            bool isFirstAdded = true;
            foreach (KeyValuePair<Guid, IHostedEditorDefinition> editorDefn in this._editors)
            {
                try
                {
                    //Ensure editor control is valid
                    Control editorControl = editorDefn.Value.Control;
                    if (null != editorControl)
                    {
                        //Add editor to leafed control
                        HostedEditor editor = new HostedEditor(editorDefn.Value.Id,
                            editorControl, editorDefn.Value.Caption, editorDefn.Value.Image);
                        this.leafedEditorHost.AddEditor(editor);

                        //Select first editor added
                        if (true == isFirstAdded)
                        {
                            this.leafedEditorHost.ActivateEditor(editorDefn.Value.Id);
                            isFirstAdded = false;
                        }
                    }
                }
                catch (Exception)
                {
                    //Ignore editor access exception
                    //(Perhaps control failed to be instantiated?)
                }
            }
        }
        /// <summary>
        /// Creates instance of specified hosted editor type from current AppDomain
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private object CreateHostedEditorInstance(Type type)
        {
            object typeInstance = null;
            try
            {
                //Instantiate type (default public ctor())
                typeInstance = Activator.CreateInstance(type);
            }
            catch (Exception)
            {
                //Ignore
            }

            return typeInstance;
        }
        /// <summary>
        /// Notifies all DslEditors when DocView has changed
        /// </summary>
        /// <param name="oldView"></param>
        /// <param name="newView"></param>
        private void OnDocDataChanged(ModelingDocData oldData, ModelingDocData newData)
        {
            //Ensure there is a change in data
            if (oldData == newData)
                return;

            //Notify controls of change in root element
            foreach (KeyValuePair<Guid, IHostedEditorDefinition> editor in this._editors)
            {
                IDslEditorControl control = editor.Value.Control as IDslEditorControl;
                if (null != control)
                {
                    if ((null == newData)
                        || (null == newData.RootElement))
                    {
                        control.OnRootElementChanged(null);
                    }
                    else
                    {
                        if (null != newData)
                            control.OnRootElementChanged(newData.RootElement);
                    }
                }
            }
        }
        #endregion
    }
}
